package com.zjm.zdk;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class FutureTaskUseCase {

    /**
     * 通过 FutureTask get() 阻塞效果演示来探索 线程执行的全过程。
     * @param args
     * @throws IOException
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        MyFutureTask<String> futureTask = new MyFutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println("执行MyFutureTask.call()，线程名称：" + Thread.currentThread().getName() + " this: " + this);
                Thread.sleep(20 * 1000);
                return "ok";
            }
        });
        Thread t1 = new Thread(futureTask);
        t1.setName("线程1");
        // 当调用线程的start()方法时，操作系统会新开一个线程，而这个线程初始化OK以后，
        // 会主动帮我们调用 Thread.run()————run()方法的调用不是我们控制的，但是无论如何，线程启动以后就会主动帮我们调用Thread.run()
        // Thread.run() 里面会调用 target.run()，而这里的target也就是 FutureTask,也就是说会调用FutureTask.run()
        // FutureTask.run() 方法里会调用 FutureTask.callable.call(), 而这里的callable 也就是我们提交的 runnable接口实现或者callable接口的实现
        // 在我们构建FutureTask的实例时必须传入一个runnable实现或者callable实现，
        // 但是无论传入的是runnable还是callable，最终都被转化为FutureTask里面的一个callable属性
        // 其实关于java对多线程的支持，最终我们抓住两个点即可 (1)Thread对象(2)Runnable接口，
        // 一定是Thread.start(),然后系统主动帮我们调用Thread.run(),run()最终帮我们实现业务代码调用
        // Thread本身只能接收一个runnable, 并不能"直接"支持callable, 除非你的callable也实现了runnable
        t1.start();

        Thread.sleep(500);

        Thread t2 = new Thread(() -> {
            try {
                System.out.println(futureTask.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                System.out.println(futureTask.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });

        Thread t4 = new Thread(() -> {
            try {
                System.out.println(futureTask.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });

        t2.setName("线程2");
        t3.setName("线程3");
        t4.setName("线程4");
        t2.start();
        t3.start();
        t4.start();

        System.in.read();
    }
}
